package ddpdecoder;
import java.sql.SQLException;
import java.util.*;
import java.io.*;
import java.nio.*;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.math.BigDecimal;
@SuppressWarnings("unused")
public class Decoder{
	//------------------------package#1---------------------------------//
	private String spec=new String("The specification from package one.");
	private byte SOH1; 												 	//0x07
	private int APIKey=0;
	private List<Byte> DataTypeByte=new ArrayList<Byte>();
	private List<String> DataType=new ArrayList<String>();
	private List<String> DataUnit=new ArrayList<String>();
	private List<String> Description=new ArrayList<String>();
	private int dataCount=0;
	//----------------------------------------------------------------- //
	private Database db=new Database();                                 //start the database as soon as decoder set
	public Decoder(){};
    public Decoder(String s){this.spec=s;};							 	//constructor constructs with specification from package#1
    
    public void decodeSpec() throws IOException, SQLException{        	//method that decodes specification package#1
    
    	int indexBegin,indexEnd;                                     	//to "cut out" the data type, data unit, and the description
    	indexBegin=indexEnd=21;                                       	//21=1+20=SOH+APIKey
    	byte[] Api_temp=new byte[20];                                   //a temporary byte array used to convert API key from char to integer
    	ByteBuffer buffer=ByteBuffer.wrap(spec.getBytes("ISO-8859-1"));
    	SOH1=buffer.get();
    	if(SOH1!=0x07) return;
    	buffer.get(Api_temp,0,20);
    	for (int i=0;i<20;i++)                                    //transform char 20 to integer
    	{
    		APIKey=(Api_temp[i]-'0')+APIKey*10;
    	}
    	if(!db.checkAPI(APIKey)){									    //check whether or not the API key exists
    		System.out.println("OOPS, it seems like this device has not been registered.");
    		return;                 				
    	}
    	byte eot=buffer.get(indexBegin);
    	while(eot!=(byte)0xFF){
    		DataTypeByte.add(buffer.get(indexBegin++));
    		DataUnit.add(spec.substring(indexBegin,indexBegin+=8));
    		indexEnd=indexBegin;
    		while(spec.charAt(indexEnd++)!='\0');    	                 //use $ for test by command line
    		Description.add(spec.substring(indexBegin, indexEnd-1)); 	 //the offset is 15, 1+4+1+8=14, and \0 is excluded by minus 1
    		indexBegin=indexEnd;
    		
    		switch(DataTypeByte.get(dataCount)){
    		 	case 0x00: DataType.add("byte"); 		 break;
    		 	case 0x01: DataType.add("short"); 	     break;
    		 	case 0x02: DataType.add("int"); 		 break;
    		 	case 0x03: DataType.add("int64"); 	     break;
    		 	case 0x04: DataType.add("unsign-short"); break;
    		 	case 0x05: DataType.add("unsign-int");   break;
    		 	case 0x06: DataType.add("unsign-int64"); break;
    		 	case 0x10: DataType.add("float"); 	     break;
    		 	case 0x11: DataType.add("double"); 	   	 break;
    		 	case 0x12: DataType.add("extended");     break;
    		 	case 0x3f: DataType.add("Test");         break; 		 //ascii(0x3f)='?'. This is a test case.
    		 	default:   DataType.add("Unsupported");    
                }
    		    dataCount++;                                             //data specification increment by one
    	        eot=buffer.get(indexBegin); 	
    	}
  	     System.out.println("Protocol package received. Transmission starts.");
    }
   
    
    public void decodeData(String s) throws IOException, SQLException{   //method that decodes data package #>=2
    	  byte SOH2;
    	  byte[] Api_temp=new byte[20];
    	  String data=new String(s);
    	  ByteBuffer buffer=ByteBuffer.wrap(data.getBytes("ISO-8859-1"));
    	  SOH2=buffer.get();
    	  if(SOH2==0x07){
    		  reconstruct(data);       	                                 // if receive another specification package, then reconstruct the decoder by this data
    		  return;
    	  }
    	  int indexData=21;
    	  int j;														 //index used to marks the number of data
    	  int apikey2=0;
    	  byte dataByte=0;
    	  short dataShort=0;
    	  int dataInt=0;
          float dataFloat=0;
    	  double dataDouble=0;
    	  long dataLong=0;
    	  buffer.get(Api_temp,0,20);
          for (int i=0;i<20;i++)                                         //transform char 20 to integer
        	{
        		apikey2=(Api_temp[i]-'0')+apikey2*10;
        	}
    	  if(SOH2!=0x08 || apikey2!=APIKey){
    		  System.out.println("APIKey doesn't match.");
    		  return;                      // added on 6/5, remember to add exception handle
    	  }
    	  for(j=0;j<dataCount;j++){
    	  buffer.position(indexData);                                    // locate the cursor
			switch (DataType.get(j)) {
			case "byte":
				dataByte = buffer.get();
				indexData = buffer.position();
				db.saveByte(APIKey, dataByte, DataUnit.get(j) , Description.get(j));
				break;
			case "short":
				dataShort = buffer.getShort();
				indexData = buffer.position();
				db.saveShort(APIKey, dataShort, DataUnit.get(j) , Description.get(j));
				break;
			case "int":
				dataInt = buffer.getInt();
				indexData = buffer.position();
				db.saveInt(APIKey, dataInt, DataUnit.get(j) , Description.get(j));
				break;
			case "int64":
				dataLong = buffer.getLong();
				indexData = buffer.position();
				db.saveLong(APIKey, dataLong, DataUnit.get(j) , Description.get(j));
				break;
			case "float":
				dataFloat = buffer.getFloat();
				indexData = buffer.position();
				db.saveFloat(APIKey, dataFloat, DataUnit.get(j) , Description.get(j));
				break;
			case "double":
				dataDouble = buffer.getDouble();
				indexData = buffer.position();
				db.saveDouble(APIKey, dataDouble, DataUnit.get(j) , Description.get(j));
				break;
			case "unsign-short":
				break;
			case "unsign-int":
				break;
			case "unsign-int64":
				break;
			case "extended":
				break;
			case "Test":
				dataDouble = buffer.getDouble();
				indexData = buffer.position();
				db.saveDouble(APIKey, dataDouble, DataUnit.get(j) , Description.get(j));
				break;
			default:
				;
    	  }
//    	  System.out.println(APIKey);
//    	  System.out.println(DataType);
//    	  System.out.println(DataUnit);
//    	  System.out.println(Description);
    	  }
    }
    
    public void reconstruct(String s) throws IOException, SQLException{
    	this.spec=s;
    	DataTypeByte.clear();
    	DataType.clear();
    	DataUnit.clear();
    	Description.clear();
    	dataCount=0;
    	APIKey=0;
    	decodeSpec();
    }
    
    public void putIp(String ip){
    	db.putIp(ip);
    }
    
    public void wipeIp(String ip){
    	db.wipeIp(ip);
    }
    
    public static void main(String[] args) throws Exception{  		      //for unit test
    Decoder d=new Decoder(); 									    
    d.decodeSpec();
    d.decodeData("0123110006781234567886086089679069");								  //remember not to type CR when using command line
    System.out.println(Charset.forName("ISO-8859-1"));
    }
}
